/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Discribe：实现音效管理器的主要逻辑
* CreateTime：2020-04-05 20:23:51
* Version：1.0
* Modify Recoder：
*************************************************/

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace TinyToolKit.Sound
{
    public class TSoundManager : ManagerBase
    {
        #region Variable
        /// <summary>
        /// 忽略重复的music的播放，避免重复创建AudioItem
        /// </summary>
        public bool IgnoreDuplicateMusic
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.ignoreDuplicateMusic; 
                return false;
            }
        }

        /// <summary>
        /// 忽略重复的Sound的播放，避免重复创建AudioItem
        /// </summary>
        public bool IgnoreDuplicateSounds
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.ignoreDuplicateSounds;
                return false; 
            }
        }

        /// <summary>
        /// 忽略重复的UISound的播放，避免重复创建AudioItem
        /// </summary>
        public bool IgnoreDuplicateUISounds
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.ignoreDuplicateUiSounds; 
                return false;
            }
        }

        /// <summary>
        /// 设置全局音量
        /// </summary>
        public float GlobalVolume
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.globalVolume;
                return 1;
            }

            set
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    TinyTool.Instance.TinyToolSetting.globalVolume = value;
            }
        }

        /// <summary>
        /// 设置全局的Music的音量
        /// </summary>
        public float GlobalMusicVolume
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.globalMusicVolume; 
                return 1;
            }
            
            set
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    TinyTool.Instance.TinyToolSetting.globalMusicVolume = value;
            }
        }

        /// <summary>
        ///  设置全局的Sound的音量
        /// </summary>
        public float GlobalFXSoundsVolume
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.globalSoundsVolume; 
                return 1;
            }
            
            set
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    TinyTool.Instance.TinyToolSetting.globalSoundsVolume = value;
            }
        }

        /// <summary>
        ///  设置全局的UISound的音量
        /// </summary>
        public float GlobalUISoundsVolume
        {
            get
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    return TinyTool.Instance.TinyToolSetting.globalUISoundsVolume;
                return 1;
            }
            
            set
            {
                if (TinyTool.Instance != null && TinyTool.Instance.TinyToolSetting)
                    TinyTool.Instance.TinyToolSetting.globalUISoundsVolume = value;
            }
        }

        private Transform _target;
        public Transform Target 
        {
            get
            {
                if (_target == null)
                {
                    GameObject target = TinyTool.Instance?.gameObject;
                    if (target == null)
                        _target = new GameObject("AudioSources").transform;
                    else
                    {
                        Transform child = new GameObject("AudioSources").transform;
                        child.SetParent(target.transform);
                        _target = child.transform;
                    }
                }

                return _target;
            }
        }

        private Dictionary<int, AudioItem> musicAudiosDic;
        private Dictionary<int, AudioItem> soundAudiosDic;
        private Dictionary<int, AudioItem> UISoundAudiosDic;
        private Dictionary<int, AudioItem> audiosPool;

        private List<int> removeKeys = new List<int>();
        
        public List<string> audiosNameInPool
        {
            get
            {
                List<string> audiosName = new List<string>();
                foreach (var item in audiosPool.Values)
                {
                    audiosName.Add(item.Clip.name);
                }
                return audiosName;
            }
        }
        #endregion

        #region Manager Func
        public override void Init()
        {
            musicAudiosDic = new Dictionary<int, AudioItem>();
            soundAudiosDic = new Dictionary<int, AudioItem>();
            UISoundAudiosDic = new Dictionary<int, AudioItem>();
            audiosPool = new Dictionary<int, AudioItem>();
        }

        /// <summary>
        /// 当加载新的场景的时候，停止并移除所有非持续的Audio
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="mode"></param>
        public override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            RemoveNonPersistAudio(musicAudiosDic);
            RemoveNonPersistAudio(soundAudiosDic);
            RemoveNonPersistAudio(UISoundAudiosDic);
            RemoveNonPersistInAudioPool();
        }

        public override void UpdateManager()
        {
            UpdateAllAudio(musicAudiosDic);
            UpdateAllAudio(soundAudiosDic);
            UpdateAllAudio(UISoundAudiosDic);
        }

        public override void Shutdown()
        {
            musicAudiosDic.Clear();
            soundAudiosDic.Clear();
            UISoundAudiosDic.Clear();
            audiosPool.Clear();
        }
        #endregion

        #region Internal Func
        /// <summary>
        /// 根据音频类型，返回对应的存储字典
        /// </summary>
        /// <param name="audioType">需要的音频类型</param>
        /// <returns>返回存储该音频的字典</returns>
        private Dictionary<int, AudioItem> GetAudioTypeDic(AudioItemType audioType)
        {
            Dictionary<int, AudioItem> audioDic = new Dictionary<int, AudioItem>();
            switch (audioType)
            {
                case AudioItemType.Music:
                    audioDic = musicAudiosDic;
                    break;
                case AudioItemType.FxSound:
                    audioDic = soundAudiosDic;
                    break;
                case AudioItemType.UISound:
                    audioDic = UISoundAudiosDic;
                    break;
            }
            return audioDic;
        }

        /// <summary>
        /// 根据音频类型，返回"是否忽视重复播放"的设置
        /// </summary>
        /// <param name="audioType"></param>
        /// <returns></returns>
        private bool GetAudioTypeIgnoreDuplicateSetting(AudioItemType audioType)
        {
            bool setting = false;
            switch (audioType)
            {
                case AudioItemType.Music:
                    setting = IgnoreDuplicateMusic;
                    break;
                case AudioItemType.FxSound:
                    setting = IgnoreDuplicateSounds;
                    break;
                case AudioItemType.UISound:
                    setting = IgnoreDuplicateUISounds;
                    break;
            }
            return setting;
        }

        /// <summary>
        /// 更新所有存储在音频字典中的音频
        /// </summary>
        /// <param name="audioDic"></param>
        private void UpdateAllAudio(Dictionary<int, AudioItem> audioDic)
        {
            if (audioDic == null || audioDic.Count <= 0) return;
            
            removeKeys.Clear();
            foreach (KeyValuePair<int, AudioItem> audio in audioDic)
            {
                AudioItem audioItem = audio.Value;
                audioItem.UpdateAudio();

                //移除不再激活(播放)的音频(即Stopped状态的音频),将audioItem存储在音频池中，以便以后调用
                if (audioItem.IsPlaying == false && audioItem.Paused == false)
                {
                    GameObject.Destroy(audioItem.AudioSource);

                    if (audiosPool.ContainsKey(audio.Key) == false)
                        audiosPool.Add(audio.Key, audioItem);
                    audioItem.Pooled = true;
                    removeKeys.Add(audio.Key);
                }
            }

            for (int i = 0; i < removeKeys.Count; i++)
            {
                audioDic.Remove(removeKeys[i]);
            }
        }

        //移除audioDic字典和音频池中所有非持续的音频
        private void RemoveNonPersistAudio(Dictionary<int, AudioItem> audioDic)
        {
            List<int> tempKey = new List<int>();
            foreach (KeyValuePair<int, AudioItem> audio in audioDic)
            {
                AudioItem audioItem = audio.Value;
                if (audioItem.Persist == false && audioItem.Activated)
                {
                    GameObject.Destroy(audioItem.AudioSource);
                    tempKey.Add(audio.Key);
                }
            }

            for (int i = 0; i < tempKey.Count; i++)
            {
                audioDic.Remove(tempKey[i]);
            }

            tempKey.Clear();
            foreach (KeyValuePair<int, AudioItem> audio in audiosPool)
            {
                AudioItem audioItem = audio.Value;
                if (audioItem.Persist == false && audioItem.Activated)
                {
                    tempKey.Add(audio.Key);
                }
            }

            for (int i = 0; i < tempKey.Count; i++)
            {
                audiosPool.Remove(tempKey[i]);
            }
        }
        
        //移除audioDic字典和音频池中所有非持续的音频
        private void RemoveNonPersistInAudioPool()
        {
            List<int> tempKey = new List<int>();
            
            foreach (KeyValuePair<int, AudioItem> audio in audiosPool)
            {
                AudioItem audioItem = audio.Value;
                if (audioItem.Persist == false && audioItem.Activated)
                {
                    tempKey.Add(audio.Key);
                }
            }

            for (int i = 0; i < tempKey.Count; i++)
            {
                audiosPool.Remove(tempKey[i]);
            }
        }

        /// <summary>
        /// 如果音频池中已经存在该音频，则移除并重新将音频添加到其对应的音频字典中
        /// </summary>
        /// <param name="audioType">该音频类型</param>
        /// <param name="audioID">该音频的ID</param>
        /// <returns>如果音频池中包含该音频，返回true，否则返回false</returns>
        public bool RestoreAudioFromPool(AudioItemType audioType, int audioID)
        {
            if (audiosPool.ContainsKey(audioID))
            {
                Dictionary<int, AudioItem> audioDict = GetAudioTypeDic(audioType);
                audioDict.Add(audioID, audiosPool[audioID]);
                audiosPool[audioID].Pooled = false;
                audiosPool.Remove(audioID);
                return true;
            }

            return false;
        }
        #endregion

        #region Get Music Audio
        public AudioItem GetMusicAudio(int audioID)
        {
            return GetAudio(AudioItemType.Music, true, audioID);
        }

        public AudioItem GetMusicAudio(AudioClip audioClip)
        {
            return GetAudio(AudioItemType.Music, true, audioClip);
        }

        public AudioItem GetFxSoundAudio(int audioID)
        {
            return GetAudio(AudioItemType.FxSound, true, audioID);
        }

        public AudioItem GetFxSoundAudio(AudioClip audioClip) 
        {
            return GetAudio(AudioItemType.FxSound, true, audioClip);
        }

        public AudioItem GetUISoundAudio(int audioID)
        {
            return GetAudio(AudioItemType.UISound, true, audioID);
        }

        public AudioItem GetUISoundAudio(AudioClip audioClip)
        {
            return GetAudio(AudioItemType.UISound, true, audioClip);
        }

        /// <summary>
        /// 主要通过audioID来获取音频
        /// </summary>
        /// <param name="audioItemType">音频类型</param>
        /// <param name="usePool">是否需要从音频池中查找</param>
        /// <param name="audioID">音频ID</param>
        /// <returns>返回该音频的AudioItem对象</returns>
        private AudioItem GetAudio(AudioItemType audioItemType, bool usePool, int audioID)
        {
            if (usePool && audiosPool.ContainsKey(audioID) && audiosPool[audioID].Type == audioItemType)
            {
                return audiosPool[audioID];
            }
            
            if (audioID == -1) return null;
            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioItemType);
            if (audioDic.ContainsKey(audioID))
                return audioDic[audioID];
            return null;
        }

        /// <summary>
        /// 主要通过AudioClip来获取音频
        /// </summary>
        /// <param name="audioItemType">音频类型</param>
        /// <param name="usePool">是否需要从音频池中查找</param>
        /// <param name="audioClip">需要查找的音频片段</param>
        /// <returns>返回该音频的AudioItem对象</returns>
        public AudioItem GetAudio(AudioItemType audioItemType, bool usePool, AudioClip audioClip)
        {
            //如果允许从缓存池中获取，则先从池里寻找是否有空余的AudioItem
            if (usePool)
            {
                foreach (KeyValuePair<int, AudioItem> audio in audiosPool)
                {
                    if (audio.Value.Clip == audioClip && audio.Value.Type == audioItemType)
                        return audio.Value;
                }
            }
            
            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioItemType);
            
            foreach (KeyValuePair<int, AudioItem> audio in audioDic)
            {
                if (audio.Value.Clip == audioClip && audio.Value.Type == audioItemType)
                    return audio.Value;
            }

            return null;
        }

        public Dictionary<int, AudioItem> GetAllAudio(AudioItemType audioItemType)
        {
            switch (audioItemType)
            {
                case AudioItemType.Music:
                    return musicAudiosDic;
                case AudioItemType.FxSound:
                    return soundAudiosDic;
                case AudioItemType.UISound:
                    return UISoundAudiosDic; 
            }
            return null;
        }
        #endregion
        
        #region Play Music Audio
        public AudioItem PlayMusic(AudioClip clip, float volume = 1, bool loop = false, bool persist = false, float fadeInSeconds = 1f, float fadeOutSeconds = 1f)
        {
            return PlayAudio(AudioItemType.Music, clip, volume, loop, persist, fadeInSeconds, fadeOutSeconds, Target);
        }

        public AudioItem PlayMusic(AudioClip clip, Transform sourceTransform, float volume = 1, bool loop = false, float fadeInSeconds = 1f, float fadeOutSeconds = 1f)
        {
            return PlayAudio(AudioItemType.Music, clip, volume, loop, false, fadeInSeconds, fadeOutSeconds, sourceTransform);
        }

        public AudioItem PlayFxSound(AudioClip clip, float volume = 1f, bool loop = false, Transform sourceTransform = null)
        {
            return PlayAudio(AudioItemType.FxSound, clip, volume, loop, false, 0f, 0f, sourceTransform);
        }

        public AudioItem PlayUISound(AudioClip clip, float volume = 1, bool loop = false)
        {
            return PlayAudio(AudioItemType.UISound, clip, volume, loop, false, 0f, 0f, null);
        }

        private AudioItem PlayAudio(AudioItemType audioType, AudioClip clip, float volume, bool loop, bool persist, float fadeInSeconds, float fadeOutSeconds, Transform sourceTransform)
        {
            AudioItem audioItem = CreateAudio(audioType, clip, volume, loop, persist, fadeInSeconds, fadeOutSeconds, sourceTransform);

            GetAudio(audioType, true, audioItem.AudioID).Play();
            return audioItem; 
        }
        
        private AudioItem CreateAudio(AudioItemType audioType, AudioClip clip, float volume, bool loop, bool persist,
            float fadeInSeconds, float fadeOutSeconds, Transform sourceTransform)
        {
            if (clip == null)
            {
                TLogTool.Error("Audio Clip不能为Null");
                return null;
            }

            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioType);
            bool ignoreDuplicateAudio = GetAudioTypeIgnoreDuplicateSetting(audioType);
            
            AudioItem duplicateAudio = GetAudio(audioType, true, clip);
            if (duplicateAudio != null )
            {
                if (duplicateAudio.Pooled)
                {
                    return duplicateAudio;
                }
                
                if (duplicateAudio.IsPlaying || duplicateAudio.Paused)
                {
                    if (ignoreDuplicateAudio)
                        return duplicateAudio;
                }
            }

            //新建Audio Item
            AudioItem audioItem = new AudioItem(this, audioType, clip, loop, persist, volume, sourceTransform);
            if (fadeInSeconds > 0 || fadeOutSeconds > 0)
                audioItem.ActiveFadeInOutEffect(fadeInSeconds, fadeOutSeconds);
            audioDic.Add(audioItem.AudioID, audioItem);
            return audioItem;
        }
        
        #endregion

        #region Stop Music
        public void Stop(AudioItemType audioType, AudioClip clip)
        {
            AudioItem audioItem = GetAudio(audioType, true, clip);
            if (audioItem != null)
                audioItem.Stop();
        }

        public void Stop(AudioItemType audioType, int audioID)
        {
            AudioItem audioItem = GetAudio(audioType, true, audioID);
            if (audioItem != null)
                audioItem.Stop();
        }

        public void StopAll()
        {
            StopAll(-1f);
        }

        public void StopAll(float musicFadeOutSeconds)
        {
            StopAllMusic(musicFadeOutSeconds);
            StopAllFxSounds();
            StopAllUISounds();
        }

        public void StopAllMusic()
        {
            StopAllAudio(AudioItemType.Music, -1);
        }

        public void StopAllMusic(float fadeOutSeconds)
        {
            StopAllAudio(AudioItemType.Music, fadeOutSeconds);
        }

        public void StopAllFxSounds()
        {
            StopAllAudio(AudioItemType.FxSound, -1);
        }

        public void StopAllUISounds()
        {
            StopAllAudio(AudioItemType.UISound, -1);
        }


        private void StopAllAudio(AudioItemType audioType, float fadeOutSeconds)
        {
            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioType);

            foreach (KeyValuePair<int, AudioItem> item in audioDic)
            {
                AudioItem audio = item.Value;
                if (fadeOutSeconds > 0)
                    audio.FadeOutSeconds = fadeOutSeconds;

                audio.Stop();
            }
        }
        #endregion

        #region Pause Music 
        public void Pause(AudioItemType audioType, AudioClip clip)
        {
            AudioItem audioItem = GetAudio(audioType, true, clip);
            if (audioItem != null)
                audioItem.Pause();
        }

        public void Pause(AudioItemType audioType, int audioID)
        {
            AudioItem audioItem = GetAudio(audioType, true, audioID);
            if (audioItem != null)
                audioItem.Pause();
        }

        public void PauseAll()
        {
            PauseAllMusic();
            PauseAllFxSounds();
            PauseAllUISounds();
        }

        public void PauseAllMusic()
        {
            PauseAllAudio(AudioItemType.Music);
        }

        public void PauseAllFxSounds()
        {
            PauseAllAudio(AudioItemType.FxSound);
        }

        public void PauseAllUISounds()
        {
            PauseAllAudio(AudioItemType.UISound);
        }

        private void PauseAllAudio(AudioItemType audioType)
        {
            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioType);

            foreach (KeyValuePair<int, AudioItem> item in audioDic)
            {
                AudioItem audio = item.Value;
                audio.Pause();
            }
        }
        #endregion

        #region Resume Audio
        public void Resume(AudioItemType audioType, AudioClip clip)
        {
            AudioItem audioItem = GetAudio(audioType, true, clip);
            if (audioItem != null)
                audioItem.Resume();
        }

        public void Resume(AudioItemType audioType, int audioID)
        {
            AudioItem audioItem = GetAudio(audioType, true, audioID);
            if (audioItem != null)
                audioItem.Resume();
        }

        public void ResumeAll()
        {
            ResumeAllMusic();
            ResumeAllSounds();
            ResumeAllUISounds();
        }

        public void ResumeAllMusic()
        {
            ResumeAllAudio(AudioItemType.Music);
        }

        public void ResumeAllSounds()
        {
            ResumeAllAudio(AudioItemType.FxSound);
        }

        public void ResumeAllUISounds()
        {
            ResumeAllAudio(AudioItemType.UISound);
        }

        private void ResumeAllAudio(AudioItemType audioType)
        {
            Dictionary<int, AudioItem> audioDic = GetAudioTypeDic(audioType);

            foreach (KeyValuePair<int, AudioItem> item in audioDic)
            {
                AudioItem audio = item.Value;
                audio.Resume();
            }
        }
        #endregion
    }
}

